technovangelist / blog / working with stdout in deno

working with stdout in deno

Today, I revisited one of my GitHub repositories, the Ollama Model Updater (https://github.com/technovangelist/ollamamodelupdater), which I hadn’t touched in a while. This project was originally built using my own Node.js library for Ollama, but Bruce from the Ollama team created a much better library that’s simply called Ollama. It works really well and is almost a drop-in replacement for my old library.

The only changes I had to make were updating some function names, such as “pull” (which I had called streamingPull) and “list” (which I called listModels). These changes were easy to make, but I realized that when pulling the latest version of a model, I wasn’t providing much information about the status and percentage. That’s when I hit a roadblock.

I couldn’t remember how to work with stdout in Deno (the JavaScript runtime). There’s an example in the Ollama JS repo for pulling a model, but it uses standard JavaScript libraries which allows you to easily go back to the beginning of the line using certain calls as shown here:

process.stdout.clearLine(0) // Clear the current line
process.stdout.cursorTo(0) // Move cursor to the beginning of the line
process.stdout.write(`${part.status} ${percent}%...`) // Write the new text

However, in Deno, cursorTo and clearLine isn’t available. So, I had to figure out how to do it.

It turns out that you need to use control characters like \n (new line) and \r (go back to the beginning of the line). This was what I needed to clear the previous status messages when pulling a model from Ollama.

When you pull a model, there are multiple layers that are downloaded, along with different status types and percentages. I wanted each status type listed next to the percentage on the same line, rather than starting a new line for each one. For example, instead of seeing “Downloading layer 1… (10%)”, then “Downloading layer 2… (11%)” and so on, I wanted it to display all the statuses on the same line: “Downloading layer 1… (10%)” and just have the percentage update.

However, sometimes the status message changes to something shorter than the previous one, leaving the end of the previous status in this new one. I’m not sure if there is a best practice for getting rid of that, but here is what I did. After printing out a status message, I set linelength to the length of that line. I then create a clear variable that is that length just filled with spaces. And that gets put in at the beginning of the string.

const clear = ` `.repeat(linelength);

Deno.stdout.write(enc(`\r${clear}\r${part.status} ${percent}%...`));

enc is a TextEncoder that was defined earlier: const enc = (s: string) => new TextEncoder().encode(s);

This approach seems to work well, as it clears out any previous status messages and prints out the new ones without extra cruft.

You have the latest starcoder2:latest
You have an outdated version of starling-lm:latest
Updating starling-lm:latest
pulling manifest
verifying sha256 digest     
writing manifest            
removing any unused layers  
success                     
You have the latest tinydolphin:latest
You have the latest tinyllama:latest
You have the latest vicuna:latest